﻿using System;
using System.ComponentModel;
using System.Diagnostics;
using System.Drawing;
using System.Linq;
using System.Reflection;
using System.Windows.Forms;
using DevExpress.CrmDemo.Win.Data;
using DevExpress.CrmDemo.Win.Helpers;
using DevExpress.XtraEditors;
using DevExpress.XtraGrid;
using DevExpress.XtraGrid.Views.Base;
using DevExpress.XtraGrid.Views.Layout;
using DevExpress.XtraGrid.Views.Layout.ViewInfo;
using DevExpress.XtraLayout;

namespace DevExpress.CrmDemo.Win.Controls {
    public partial class OpportunitiesByStage : DevExpress.XtraEditors.XtraUserControl {
        #region Constructor
        public OpportunitiesByStage() {
            InitializeComponent();

            ConfigureOppCol(oppColLead, "Lead", "[Stage] = 'Lead'");
            ConfigureOppCol(oppColQualification, "Qualification", "[Stage] = 'Qualification'");
            ConfigureOppCol(oppColNeedsAnalysis, "NeedsAnalysis", "[Stage] = 'NeedsAnalysis'");
            ConfigureOppCol(oppColProposed, "Proposed", "[Stage] = 'Proposed'");
            ConfigureOppCol(oppColNegotiation, "Negotiation", "[Stage] = 'Negotiation'");
        }

        void ConfigureOppCol(OpportunityColumn col, string tag, string filter) {
            col.LayoutView.ActiveFilterString = filter;

            col.LayoutView.MouseDown += new MouseEventHandler(layoutView_MouseDown);
            col.LayoutView.MouseMove += new MouseEventHandler(layoutView_MouseMove);
            col.LayoutView.MouseUp += new MouseEventHandler(layoutView_MouseUp);

            col.GridControl.DragDrop += new DragEventHandler(gridControl_DragDrop);
            col.GridControl.DragOver += new DragEventHandler(gridControl_DragOver);
            col.GridControl.DragLeave += new EventHandler(gridControl_DragLeave);
            col.GridControl.GiveFeedback += new GiveFeedbackEventHandler(gridControl_GiveFeedback);
            col.GridControl.QueryContinueDrag += new QueryContinueDragEventHandler(gridControl_QueryContinueDrag);
            col.GridControl.Tag = tag;
        }

        #endregion

        #region OpportunitiesDataTable property
        public BindingList<Opportunity> Opportunities {
            get { return this.opportunities; }
            set {
                this.opportunities = value;

                this.oppColLead.GridControl.DataSource = this.opportunities;
                this.oppColQualification.GridControl.DataSource = this.opportunities;
                this.oppColNeedsAnalysis.GridControl.DataSource = this.opportunities;
                this.oppColProposed.GridControl.DataSource = this.opportunities;
                this.oppColNegotiation.GridControl.DataSource = this.opportunities;
            }
        }
        #endregion

        #region Private Event handlers
        private void layoutView_MouseDown(object sender, MouseEventArgs e) {
            this.dragBoundsRectangle = null;

            if (e.Button == MouseButtons.Left) {
                var view = sender as LayoutView;
                var hitInfo = view.CalcHitInfo(e.Location);

                // If the clicked point belongs to a card caption...
                if (hitInfo.HitCard != null) {
                    this.dragBoundsRectangle = CalculateRectangle(e.Location, SystemInformation.DragSize);
                    // StartCardDrag(view, e.Location, hitInfo.RowHandle, hitInfo.HitCard.Bounds);
                }
            }
        }

        private void StartCardDrag(LayoutView view, Point clickLocation, int rowHandle, Rectangle rowBounds) {
            string id = view.GetRowCellValue(rowHandle, view.Columns["ID"]).ToString();

            // Create a cursor from the card bitmap.
            using (var gridBitmap = new Bitmap(view.GridControl.Width, view.GridControl.Height)) {
                view.GridControl.DrawToBitmap(gridBitmap,
                                              new Rectangle(0, 0, view.GridControl.Width,
                                                            view.GridControl.Height));
                using (var cardBitmap = gridBitmap.CropBitmap(rowBounds)) {
                    this.customCursorWrapper =
                        CustomCursorWrapper.CreateFromBitmap(cardBitmap,
                                                             Point.Subtract(clickLocation, new Size(rowBounds.Location)));
                }
            }

            // Start drag and drop
            view.GridControl.DoDragDrop(id, DragDropEffects.Move);
        }

        private void layoutView_MouseMove(object sender, MouseEventArgs e) {
            if (e.Button == MouseButtons.Left && this.dragBoundsRectangle.HasValue) {
                // Outside bounds... drag has officially started
                if (!this.dragBoundsRectangle.Value.Contains(e.Location)) {
                    var view = sender as LayoutView;
                    var hitInfo = view.CalcHitInfo(e.Location);

                    // If the move point belongs to a card caption...
                    if (hitInfo.HitCard != null) {
                        StartCardDrag(view, e.Location, hitInfo.RowHandle, hitInfo.HitCard.Bounds);
                    }
                    this.dragBoundsRectangle = null;
                }
            }
        }

        private void layoutView_MouseUp(object sender, MouseEventArgs e) {
            if (e.Button == MouseButtons.Left && this.dragBoundsRectangle.HasValue) {
                if (this.dragBoundsRectangle.Value.Contains(e.Location)) {
                    // It's a click... 
                    var view = sender as LayoutView;
                    var hitInfo = view.CalcHitInfo(e.Location);
                    if (hitInfo.HitCard != null) {
                        Debug.WriteLine("Mouse_Up detects click.." + (sender as LayoutView).Name + ", " + e.X + ", " + e.Y);
                        var id = (int)(view.GetRowCellValue(hitInfo.RowHandle, view.Columns["ID"]));

                        bool goToDetail;

                        Opportunity opp = this.Opportunities.FirstOrDefault(o => o.ID == id);

                        // Display form with row bound to card... position dialog at lower left corner.
                        using (var dialog = new OpportunitiesEditorForm(opp)) {
                            dialog.NavRequest += (s, a) => {
                                OnNavRequest(a);
                            };
                            dialog.ShowDialog();

                            goToDetail = dialog.GoToDetail;
                        }

                        if (goToDetail)
                            OnNavRequest(new NavRequestEventArgs(States.OppDetail, opp.ID));
                    }

                }
                this.dragBoundsRectangle = null;
            }
        }

        private void gridControl_DragOver(object sender, DragEventArgs e) {
            var gridControl = sender as GridControl;

            // Obtain the view over which the card is being dragged.
            Point point = gridControl.PointToClient(new Point(e.X, e.Y));
            LayoutView view = gridControl.GetViewAt(point) as LayoutView;

            // Valid drop location if over a LayoutView
            if (view != null) {
                SetColumnHoverColor(gridControl, true);

                e.Effect = DragDropEffects.Move;
                if (null != this.customCursorWrapper && Cursor.Current != this.customCursorWrapper.Cursor) {
                    Cursor.Current = this.customCursorWrapper.Cursor;
                    Cursor.Position.Offset(point);
                    Debug.WriteLine("Set cursor to custom icon.");
                }
                Debug.WriteLine("Dragging over " + gridControl.Name + " " + e.Effect);
            } else {
                e.Effect = DragDropEffects.None;
            }
        }

        void gridControl_DragLeave(object sender, EventArgs e) {
            var gridControl = sender as GridControl;

            SetColumnHoverColor(gridControl, false);
        }

        void SetColumnHoverColor(GridControl gridControl, bool hovering) {
            LayoutView view = (LayoutView)gridControl.MainView;
            OpportunityColumn oppCol = (OpportunityColumn)gridControl.Parent;

            if (hovering) {
                view.Appearance.ViewBackground.BackColor = Color.FromArgb(61, 61, 61);
                oppCol.BackColor = Color.FromArgb(61, 61, 61);
            } else {
                view.Appearance.ViewBackground.BackColor = Color.FromArgb(51, 51, 51);
                oppCol.BackColor = Color.FromArgb(51, 51, 51);
            }
        }

        private void gridControl_DragDrop(object sender, DragEventArgs e) {
            string dragData = e.Data.GetData(DataFormats.Text).ToString();

            // Lookup dragged row with dragged ID in source table.
            var rowDropped = this.opportunities.AsEnumerable().FirstOrDefault(row => row.ID.ToString() == dragData);
            if (rowDropped != null) {
                GridControl gridControl = sender as GridControl;
                Debug.WriteLine("Dropping row " + dragData + " over " + gridControl.Name);
                // Set source row state to state of dropped grid.
                rowDropped.Stage = gridControl.Tag.ToString().ToEnum(Stage.Lead);
                gridControl.MainView.RefreshData();
                ReleaseCursor();

                SetColumnHoverColor(gridControl, false);
            }
        }

        private void gridControl_GiveFeedback(object sender, GiveFeedbackEventArgs e) {
            e.UseDefaultCursors = e.Effect != DragDropEffects.Move;
            Debug.WriteLine("GiveFeedback " + ((Control)sender).Name + " " + e.UseDefaultCursors);
        }

        private void gridControl_QueryContinueDrag(object sender, QueryContinueDragEventArgs e) {
            Debug.WriteLine("QueryContinueDrag " + ((Control)sender).Name);
            if (!IsLeftMouseButtonDown(e.KeyState)) {
                ReleaseCursor();        // Left mouse button has been released - drag cancelled, release resources
            }
        }

        // This is required to customize the border color of the TableLayoutPanel
        private void tableLayoutPanelRoot_CellPaint(object sender, TableLayoutCellPaintEventArgs e) {
            Graphics g = e.Graphics;
            Rectangle r = e.CellBounds;

            using (Pen pen = new Pen(Color.FromArgb(77, 77, 77), 0)) {
                pen.Alignment = System.Drawing.Drawing2D.PenAlignment.Center;
                pen.DashStyle = System.Drawing.Drawing2D.DashStyle.Solid;

                if (e.Row == (tableLayoutPanelRoot.RowCount - 1)) {
                    r.Height -= 1;
                }

                if (e.Column == (tableLayoutPanelRoot.ColumnCount - 1)) {
                    r.Width -= 1;
                }

                e.Graphics.DrawRectangle(pen, r);
            }
        }

        public event NavRequestEventHandler NavRequest;

        protected void OnNavRequest(NavRequestEventArgs args) {
            if (NavRequest != null)
                NavRequest(this, args);
        }
        #endregion

        #region Helper methods
        private static Rectangle CalculateRectangle(Point centerPoint, Size size) {
            var x = centerPoint.X - (size.Width / 2);
            var y = centerPoint.Y - (size.Height / 2);
            return new Rectangle(x, y, size.Width, size.Height);
        }

        private static bool IsLeftMouseButtonDown(int keyState) {
            return (keyState & 1) != 0;
        }

        private void ReleaseCursor() {
            if (this.customCursorWrapper != null) {
                Debug.WriteLine("Releasing cursor.");
                this.customCursorWrapper.Dispose();
                this.customCursorWrapper = null;
            }
        }
        #endregion

        #region Private Data
        private BindingList<Opportunity> opportunities;
        private CustomCursorWrapper customCursorWrapper = null;
        private Rectangle? dragBoundsRectangle = null;
        #endregion
    }
}
